#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#ifdef WIN32
#include "windows.h"
#endif

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#include "pthread.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "BList.h"
#include "BVector.h"
#include "geometrics.h"
#include "quaternion.h"

#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"

#include "myglutaux.h"
#include "FTGLPolygonFont.h"
#include "AWLVP_DistanceMatrix.h"
#include "AWLocalViewport.h"
#include "AWLVP_NOOSFTerm.h"

#include "AWEntity.h"
#include "AWMessage.h"
#include "AWBody.h"
#include "AWMsgMoveBody.h"
#include "AWNOOSCBBody.h"

#include "CaseBaseUtils.h"

#include "loader.h"


extern int SCREEN_DX,SCREEN_DY;

AW_LVP_NOOSFTerm::AW_LVP_NOOSFTerm(int x,int y,int dx,int dy,FeatureTerm *ft,FTKBase *dm) : AW_LocalViewport(x,y,dx,dy)
{
	m_font=0;

	m_ft=ft;
	m_domainmodel=dm;

	m_zoom=1;
	m_desired_zoom=1;

	m_center_x=0;
	m_center_y=0;

	if (m_ft!=0) {
		List<FeatureTerm> *l;
		int i;

		ft->nodes(&l);

		m_nnodes=l->Length();
		m_nodes=new FeatureTerm *[m_nnodes];
		m_x=new float[m_nnodes];
		m_y=new float[m_nnodes];
		m_dx=new float[m_nnodes];
		m_dy=new float[m_nnodes];

		for(i=0;i<m_nnodes;i++) {
			m_nodes[i]=new FeatureTerm(l->operator [](i));
			m_x[i]=0;
			m_y[i]=0;
			m_dx[i]=0;
			m_dy[i]=0;
		} /* for */ 

		while(!l->EmptyP()) l->ExtractIni();
		delete l;
	} else {
		m_nnodes=0;
		m_nodes=0;
		m_x=0;
		m_y=0;
		m_dx=0;
		m_dy=0;
	} /* if */ 

} /* AW_LVP_NOOSFTerm::AW_LVP_NOOSFTerm */ 


AW_LVP_NOOSFTerm::~AW_LVP_NOOSFTerm()
{
	m_ft=0;
	m_domainmodel=0;

	if (m_nnodes!=0) {
		int i;

		for(i=0;i<m_nnodes;i++) {
			delete m_nodes[i];
			m_nodes[i]=0;
		} /* for */ 
		delete []m_nodes;
		m_nodes=0;
		delete []m_x;
		m_x=0;
		delete []m_y;
		m_y=0;
		delete []m_dx;
		m_dx=0;
		delete []m_dy;
		m_dy=0;
		m_nnodes=0;
	} /* if */ 

#ifndef KITSCHY_DEBUG_MEMORY 
	// I don't know why, but the memory_manager complains about this free:
	if (m_font!=0) delete m_font;
#endif
} /* AW_LVP_NOOSFTerm::~AW_LVP_NOOSFTerm */ 



void AW_LVP_NOOSFTerm::draw_internal(void)
{
	float f=1.0F;
	int vpx=0,vpy=0,vpw=800,vph=600;

	if (m_font==0) {
		FILE *fp=fopen("arial.ttf","r");
		if (fp!=0) {
			fclose(fp);
			m_font=new FTGLPolygonFont("arial.ttf");
			m_font->FaceSize(2);
			m_font->Depth(0.2F);
		} else {
			return;
		} /* if */ 
	} /* if */ 

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	{
		int tmp[4];
		glGetIntegerv(GL_VIEWPORT,tmp);
		vpx=tmp[0];
		vpy=tmp[1];
		vpw=tmp[2];
		vph=tmp[3];
		f=float(vpw)/float(vph);
	}

	glDisable(GL_DEPTH_TEST);

	/* blended border: */ 
	{
		int i;
		float f;

		glEnable(GL_BLEND);
		for(i=0;i<10;i++) {
			f=1-(i*0.01F);
			glColor4f(0,0,0,0.2F);
			glBegin(GL_QUADS);
			glVertex3f(-f,-f,0);
			glVertex3f(f,-f,0);
			glVertex3f(f,f,0);
			glVertex3f(-f,f,0);
			glEnd();
		} /* for */ 
		glDisable(GL_BLEND);
	}

	if (m_ft!=0) {

		if (m_zoom<m_desired_zoom) {
			if (m_zoom*1.075F>=m_desired_zoom) m_zoom=m_desired_zoom;
										 else m_zoom*=1.075F;
		} /* if */ 

		if (m_zoom>m_desired_zoom) {
			if (m_zoom/1.075F<=m_desired_zoom) m_zoom=m_desired_zoom;
										 else m_zoom/=1.075F;
		} /* if */ 

		{
			int i;
			float x,y;
			float dx,dy,tmp_dy;
			float bbox[4]={0,0,0,0};
			float f;
			FeatureTerm *node;
			float llx,lly,llz,urx,ury,urz;
			float ldx,ldy;
			Sort *sort;
			Symbol *name;
			char tmp[256];

			compute_position(0);

			for(i=0;i<m_nnodes;i++) {
				if (m_x[i]<bbox[0]) bbox[0]=m_x[i];
				if (m_x[i]+m_dx[i]>bbox[2]) bbox[2]=m_x[i]+m_dx[i];
				if (m_y[i]-m_dy[i]<bbox[1]) bbox[1]=m_y[i]-m_dy[i];
				if (m_y[i]>bbox[3]) bbox[3]=m_y[i];
			} /* for */ 

			glPushMatrix();
			f=1.0F;
			if ((bbox[2]-bbox[0])>f) f=bbox[2]-bbox[0];
			if ((bbox[3]-bbox[1])>f) f=bbox[3]-bbox[1];
			glTranslatef(-m_center_x/10,-m_center_y/10,0);
			glScalef(m_zoom*1.75/f,m_zoom*1.75/f,m_zoom*1.75/f);
			glTranslatef(-(bbox[0]+bbox[2])/2,-(bbox[1]+bbox[3])/2,0);

			glNormal3f(0,0,1);

			for(i=0;i<m_nnodes;i++) {
				node=m_nodes[i];
				x=m_x[i];
				y=m_y[i];
				dx=m_dx[i];
				dy=m_dy[i];

				sort=node->getSort();
				name=node->getName();

				if (node->isInt()) {
					if (name!=0) {
						sprintf(tmp," %i (%s) ",node->getInt(),name->get());
					} else {
						sprintf(tmp," %i ",node->getInt());
					} /* if */ 
					m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
					ldx=(urx-llx)*0.05F+0.1F;
					ldy=(ury-lly)*0.05F;
					glPushMatrix();
					glTranslatef(x,y,0);
					glColor3f(0.4F,0.4F,0.4F);
					glPushMatrix();
					glTranslatef(dx/2,-ldy,0);
					glutSolidBox(dx/2,ldy,0.1F);
					glPopMatrix();
					glScalef(0.05F,0.05F,0.05F);
					glTranslatef(0,-2.0F,0);
					glColor3f(1,1,1);
					m_font->Render(tmp);
					glPopMatrix();

					if (ldx>dx) dx=ldx;
					if (ldy*2>dy) dy=ldy*2;
				} /* if */ 

				if (node->isFloat()) {
					if (name!=0) {
						sprintf(tmp," %f (%s) ",node->getFloat(),name->get());
					} else {
						sprintf(tmp," %f ",node->getFloat());
					} /* if */ 
					m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
					ldx=(urx-llx)*0.05F+0.1F;
					ldy=(ury-lly)*0.05F;
					glPushMatrix();
					glTranslatef(x,y,0);
					glColor3f(0.4F,0.4F,0.4F);
					glPushMatrix();
					glTranslatef(dx/2,-ldy,0);
					glutSolidBox(dx/2,ldy,0.1F);
					glPopMatrix();
					glScalef(0.05F,0.05F,0.05F);
					glTranslatef(0,-2.0F,0);
					glColor3f(1,1,1);
					m_font->Render(tmp);
					glPopMatrix();

					if (ldx>dx) dx=ldx;
					if (ldy*2>dy) dy=ldy*2;
				} /* if */ 

				if (node->isSymbol()) {
					if (name!=0) {
						sprintf(tmp," %s (%s) ",node->getSymbol()->get(),name->get());
					} else {
						sprintf(tmp," %s ",node->getSymbol()->get());
					} /* if */ 
					m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
					ldx=(urx-llx)*0.05F+0.1F;
					ldy=(ury-lly)*0.05F;
					glPushMatrix();
					glTranslatef(x,y,0);
					glColor3f(0.4F,0.4F,0.4F);
					glPushMatrix();
					glTranslatef(dx/2,-ldy,0);
					glutSolidBox(dx/2,ldy,0.1F);
					glPopMatrix();
					glScalef(0.05F,0.05F,0.05F);
					glTranslatef(0,-2.0F,0);
					glColor3f(1,1,1);
					m_font->Render(tmp);
					glPopMatrix();

					if (ldx>dx) dx=ldx;
					if (ldy*2>dy) dy=ldy*2;
				} /* if */ 

				if (node->isFeatureTerm()) {
					List<Symbol> *f_names;
					Symbol *f_name;
					FeatureTerm *ft;
					float yoffset=0;
					float features_offset=0;

					tmp_dy=0;

					if (name!=0) {
						sprintf(tmp,"%s",name->get());
						m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
						ldx=(urx-llx)*0.05F+0.2F;
						ldy=(ury-lly)*0.05F;
						glPushMatrix();
						glTranslatef(x,y,0);
						glColor3f(0.6F,0.1F,0.1F);
						glPushMatrix();
						glTranslatef(dx/2,-ldy,0);
						glutSolidBox(dx/2,ldy,0.1F);
						glPopMatrix();
						glScalef(0.05F,0.05F,0.05F);
						glTranslatef(0,-2.0F,0);
						glColor3f(1,1,1);
						m_font->Render(tmp);
						glPopMatrix();

						if (ldx>dx) dx=ldx;
						yoffset+=ldy*2;
						tmp_dy+=ldy*2;
					} /* if */ 

					sprintf(tmp," %s ",(sort!=0 ? sort->get():"sort?"));
					m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
					ldx=(urx-llx)*0.05F+0.2F;
					ldy=(ury-lly)*0.05F;
					glPushMatrix();
					glTranslatef(x,y-yoffset,0);
					glColor3f(0.4F,0.6F,1.0F);
					glPushMatrix();
					glTranslatef(dx/2,-ldy,0);
					glutSolidBox(dx/2,ldy,0.1F);
					glPopMatrix();
					glScalef(0.05F,0.05F,0.05F);
					glTranslatef(0,-2.0F,0);
					glColor3f(1,1,1);
					m_font->Render(tmp);
					glPopMatrix();

					if (ldx>dx) dx=ldx;
					yoffset+=ldy*2;
					tmp_dy+=ldy*2;

					features_offset=yoffset;

					f_names=sort->getFeatures();
					f_names->Rewind();
					while(f_names->Iterate(f_name)) {
						ft=node->featureValue(f_name);
						if (ft!=0) {
							sprintf(tmp," - %s ",f_name->get());
							m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
							ldx=(urx-llx)*0.05F+0.2F;
							ldy=(ury-lly)*0.05F;
							glPushMatrix();
							glTranslatef(x,y-yoffset,0);
							glColor3f(0.24F,0.35F,0.6F);
							glPushMatrix();
							glTranslatef(dx/2,-ldy,0);
							glutSolidBox(dx/2,ldy,0.1F);
							glPopMatrix();
							glScalef(0.05F,0.05F,0.05F);
							glTranslatef(0,-2.0F,0);
							glColor3f(1,1,1);
							m_font->Render(tmp);
							glPopMatrix();

							yoffset+=ldy*2;
							tmp_dy+=ldy*2;

							if (ldx>dx) dx=ldx;
						} /* if */ 
						delete ft;
					} /* while */  

					/* Draw Arrow: */ 
					yoffset=features_offset;
					f_names->Rewind();
					while(f_names->Iterate(f_name)) {
						ft=node->featureValue(f_name);
						if (ft!=0) {
							int j;
							float x1,x2,y1,y2;
							float v1,v2,w1,w2,n;

							sprintf(tmp," - %s ",f_name->get());
							m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
							ldx=(urx-llx)*0.05F;
							ldy=(ury-lly)*0.05F;

							for(j=0;j<m_nnodes;j++) {
								if (m_nodes[j]->same(ft)) {

									x1=m_x[i]+ldx+0.05F;
									y1=m_y[i]-yoffset-0.05F;
									x2=m_x[j];
									y2=m_y[j]-m_dy[j]/2;

									v1=x2-x1;
									v2=y2-y1;
									n=float(sqrt(v1*v1+v2*v2));
									v1/=n;
									v2/=n;

									w1=v2;
									w2=-v1;
									n=float(sqrt(w1*w1+w2*w2));
									w1/=n;
									w2/=n;

									glBegin(GL_LINES);
									glColor3f(1,0,0);
									glVertex3f(x1,y1,0);
									glVertex3f(x2,y2,0);
									glEnd();

									glBegin(GL_TRIANGLES);
									glColor3f(1,0,0);
									glVertex3f(x2,y2,0);
									glVertex3f(x2-v1*0.1F+w1*0.05F,y2-v2*0.1F+w2*0.05F,0);
									glVertex3f(x2-v1*0.1F-w1*0.05F,y2-v2*0.1F-w2*0.05F,0);
									glEnd();
								} /* if */ 
							} /* for */ 
							yoffset+=ldy*2;

						} /* if */ 
						delete ft;
					} /* while */ 

					delete f_names;

					if (tmp_dy>dy) dy=tmp_dy;	
				} /* if */ 

				if (node->isSet()) {
					int j;

					sprintf(tmp," set ");
					m_font->BBox(tmp, llx, lly, llz, urx, ury, urz);	
					ldx=(urx-llx)*0.05F+0.1F;
					ldy=(ury-lly)*0.05F;
					glPushMatrix();
					glTranslatef(x,y,0);
					glColor3f(0.2F,0.2F,0.2F);
					glPushMatrix();
					glTranslatef(dx/2,-ldy,0);
					glutSolidBox(dx/2,ldy,0.1F);
					glPopMatrix();
					glScalef(0.05F,0.05F,0.05F);
					glTranslatef(0,-2.0F,0);
					glColor3f(1,1,1);
					m_font->Render(tmp);
					glPopMatrix();

					for(j=0;j<m_nnodes;j++) {
						if (is_value_of(m_nodes[j],m_nodes[i])) {
							float x1,x2,y1,y2;
							float v1,v2,w1,w2,n;

							x1=m_x[i]+ldx;
							y1=m_y[i]-m_dy[i]/2;
							x2=m_x[j];
							y2=m_y[j]-m_dy[j]/2;

							v1=x2-x1;
							v2=y2-y1;
							n=float(sqrt(v1*v1+v2*v2));
							v1/=n;
							v2/=n;

							w1=v2;
							w2=-v1;
							n=float(sqrt(w1*w1+w2*w2));
							w1/=n;
							w2/=n;

							glBegin(GL_LINES);
							glColor3f(1,0,0);
							glVertex3f(x1,y1,0);
							glVertex3f(x2,y2,0);
							glEnd();

							glBegin(GL_TRIANGLES);
							glColor3f(1,0,0);
							glVertex3f(x2,y2,0);
							glVertex3f(x2-v1*0.1F+w1*0.05F,y2-v2*0.1F+w2*0.05F,0);
							glVertex3f(x2-v1*0.1F-w1*0.05F,y2-v2*0.1F-w2*0.05F,0);
							glEnd();
						} /* if */ 
					} /* for */ 

					if (ldx>dx) dx=ldx;
					if (ldy*2>dy) dy=ldy*2;
				} /* if */ 

				m_dx[i]=dx;
				m_dy[i]=dy;

			} /* for */ 

			glPopMatrix();

		}

		/* Close button: */ 
		{
			glPushName(1);
			glPushMatrix();
			glTranslatef(0.7F,-0.85F,0);
			glColor3f(0.4F,0.6F,1.0F);
			glutSolidBox(0.20F,0.06F,0.1F);
			glTranslatef(0,0,-0.2F);
			{
				float llx,lly,llz,urx,ury,urz;
				m_font->BBox( "Close", llx, lly, llz, urx, ury, urz);		
				glPushMatrix();
				glScalef(0.03F,0.03F,0.03F);
				glTranslatef(-(urx-llx)/2,-0.5F,0);
				glColor3f(1,1,1);
				m_font->Render("Close");
				glPopMatrix();
			}
			glPopMatrix();
			glPopName();
		}

		/* Zoom in/out: */ 
		{
			glPushName(2);
			glPushMatrix();
			glTranslatef(0.33F,-0.775F,0);
			glColor3f(0.4F,0.6F,1.0F);
			glutSolidBox(0.075F,0.05F,0.1F);
			glTranslatef(0,0,-0.2F);
			{
				float llx,lly,llz,urx,ury,urz;
				m_font->BBox( "In", llx, lly, llz, urx, ury, urz);		
				glPushMatrix();
				glScalef(0.03F,0.03F,0.03F);
				glTranslatef(-(urx-llx)/2,-0.5F,0);
				glColor3f(1,1,1);
				m_font->Render("In");
				glPopMatrix();
			}
			glPopMatrix();
			glPopName();
		}
		{
			glPushName(3);
			glPushMatrix();
			glTranslatef(0.33F,-0.925F,0);
			glColor3f(0.4F,0.6F,1.0F);
			glutSolidBox(0.075F,0.05F,0.1F);
			glTranslatef(0,0,-0.2F);
			{
				float llx,lly,llz,urx,ury,urz;
				m_font->BBox( "Out", llx, lly, llz, urx, ury, urz);		
				glPushMatrix();
				glScalef(0.03F,0.03F,0.03F);
				glTranslatef(-(urx-llx)/2,-0.5F,0);
				glColor3f(1,1,1);
				m_font->Render("Out");
				glPopMatrix();
			}
			glPopMatrix();
			glPopName();
		}

		/* Arrows: */ 
		{
			glPushName(4);
			glColor3f(0.4F,0.6F,1.0F);
			glBegin(GL_TRIANGLES);
			glVertex3f(-0.6,-0.7,-0.1);
			glVertex3f(-0.5,-0.8,-0.1);
			glVertex3f(-0.6,-0.9,-0.1);
			glEnd();		
			glPopName();

			glPushName(5);
			glColor3f(0.4F,0.6F,1.0F);
			glBegin(GL_TRIANGLES);
			glVertex3f(-0.6,-0.9,-0.1);
			glVertex3f(-0.7,-1.0,-0.1);
			glVertex3f(-0.8,-0.9,-0.1);
			glEnd();		
			glPopName();

			glPushName(6);
			glColor3f(0.4F,0.6F,1.0F);
			glBegin(GL_TRIANGLES);
			glVertex3f(-0.8,-0.9,-0.1);
			glVertex3f(-0.9,-0.8,-0.1);
			glVertex3f(-0.8,-0.7,-0.1);
			glEnd();		
			glPopName();

			glPushName(7);
			glColor3f(0.4F,0.6F,1.0F);
			glBegin(GL_TRIANGLES);
			glVertex3f(-0.8,-0.7,-0.1);
			glVertex3f(-0.7,-0.6,-0.1);
			glVertex3f(-0.6,-0.7,-0.1);
			glEnd();		
			glPopName();

		}
	} /* if */ 

	glEnable(GL_DEPTH_TEST);

	glPopMatrix();

} /* AW_LVP_NOOSFTerm::draw_internal */ 




bool AW_LVP_NOOSFTerm::is_value_of(FeatureTerm *f1,FeatureTerm *f2)
{
	List<FeatureTerm> *l;
	List<Symbol> *f_names;
	Symbol *f_name;
	FeatureTerm *f;

	if (f2->isFeatureTerm()) {
		f_names=f2->getSort()->getFeatures();
		f_names->Rewind();
		while(f_names->Iterate(f_name)) {
			f=f2->featureValue(f_name);
			if (f1->same(f)) {
				delete f;
				delete f_names;
				return true;
			} /* if */ 
			delete f;
		} /* while */ 
		delete f_names;
	} /* if */ 

	if (f2->isSet()) {
		l=f2->set_values();

		if (l!=0 && l->MemberP(f1)) {
			delete l;
			return true;
		} /* if */ 

		delete l;
	} /* if */ 

	return false;
} /* AW_LVP_NOOSFTerm::is_value_of */ 



void AW_LVP_NOOSFTerm::compute_position(int id)
{
	int i;
	int n_sons=0;
	float yoffs,tmp;

	for(i=id+1;i<m_nnodes;i++) 
		if (is_value_of(m_nodes[i],m_nodes[id])) n_sons++;

	if (n_sons==0) {
		m_x[id]=0;
		m_y[id]=0;
	} else {

		yoffs=0;

		for(i=id+1;i<m_nnodes;i++) {
			if (is_value_of(m_nodes[i],m_nodes[id])) {
				compute_position(i);
				tmp=(m_y[i]-(m_dy[i]/2))*2;
				move_subtree(i,m_dx[id]+0.2F,yoffs);
				yoffs+=tmp-0.2F;
			} /* if */ 
		} /* if */ 

		m_x[id]=0;
		m_y[id]=(yoffs/2)+(m_dy[id]/2);
	} /* if */ 

} /* AW_LVP_NOOSFTerm::compute_position */ 



void AW_LVP_NOOSFTerm::move_subtree(int id,float x,float y)
{
	int i;

	m_x[id]+=x;
	m_y[id]+=y;

	for(i=id+1;i<m_nnodes;i++) 
		if (is_value_of(m_nodes[i],m_nodes[id])) move_subtree(i,x,y);
} /* AW_LVP_NOOSFTerm::move_subtree */ 





bool AW_LVP_NOOSFTerm::MouseClick_internal(int selected)
{

	switch(selected) {
	case 1: m_ft=0;
			break;
	case 2: /* zoom in */ 
			if (m_desired_zoom<16) m_desired_zoom*=1.25;	
			if (m_desired_zoom>=16) m_desired_zoom=16;
			break;

	case 3: /* zoom out: */ 
			if (m_desired_zoom>1) m_desired_zoom/=1.25;
			if (m_desired_zoom<=1) m_desired_zoom=1;
			break;
	case 4: /* right: */ 
			m_center_x+=1;
			break;
	case 5: /* down: */ 
			m_center_y-=1;
			break;
	case 6: /* left: */ 
			m_center_x-=1;
			break;
	case 7: /* up: */ 
			m_center_y+=1;
			break;
	default:
			break;
	} /* switch */ 
	
	return true;
} /* AW_LVP_NOOSFTerm::MouseClick */ 


FeatureTerm *AW_LVP_NOOSFTerm::get_ft(void)
{
	return m_ft;
} /* AW_LVP_NOOSFTerm::get_ft */ 

